package com.codename1.samples;


import static com.codename1.ui.CN.*;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Dialog;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import com.codename1.ui.Toolbar;
import com.codename1.io.ConnectionRequest;
import com.codename1.io.Cookie;
import com.codename1.io.JSONParser;
import com.codename1.io.Log;
import com.codename1.io.NetworkManager;
import com.codename1.io.Util;
import com.codename1.testing.AbstractTest;
import com.codename1.testing.TestUtils;
import com.codename1.ui.BrowserComponent;
import com.codename1.ui.Button;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import java.util.Map;

// Test scripts available at https://github.com/shannah/cn1-tests-server-side-scripts/tree/master/cookie
// Should be run on a server that supports PHP
/**
 * This file was generated by <a href="https://www.codenameone.com/">Codename One</a> for the purpose 
 * of building native mobile applications using Java.
 */
public class CookiesTest {

    private static final String BASE_URL = "http://solutions.weblite.ca/cn1tests/cookie";
    private static final String BASE_URL_WITH_PORT = "http://solutions.weblite.ca:80/cn1tests/cookie";
    //private static final String BASE_URL = "http://10.0.1.34:62355/cookie";
    private Form current;
    private Resources theme;

    public void init(Object context) {
        // use two network threads instead of one
        updateNetworkThreadCount(2);

        theme = UIManager.initFirstTheme("/theme");

        // Enable Toolbar on all Forms by default
        Toolbar.setGlobalToolbar(true);

        // Pro only feature
        Log.bindCrashProtection(true);

        addNetworkErrorListener(err -> {
            // prevent the event from propagating
            err.consume();
            if(err.getError() != null) {
                Log.e(err.getError());
            }
            Log.sendLogAsync();
            Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null);
        });        
    }
    
    public void start() {
        if(current != null){
            current.show();
            return;
        }
        Form hi = new Form("Test Cookies", BoxLayout.y());
        Button runTests = new Button("Run Test");
        runTests.addActionListener(e->{
            callSerially(()->{
                runTest(BASE_URL);
                
                // We need to run the test also with explicit port number
                // because https://github.com/codenameone/CodenameOne/issues/2804
                runTest(BASE_URL_WITH_PORT);
                
            });
        });
        hi.add(runTests);
        hi.show();
    }

    private void runTest(String baseUrl) {
        try {
            // First test without explicit port
            UnitTest test = new UnitTest(baseUrl);
            if (test.runTest()) {
                Dialog.show("Passed", "The test passed", "OK", null);
            } else {

                Throwable t = test.getException();
                if (t != null) {
                    Log.e(test.getException());
                    Dialog.show("Failed", "The test failed with exception: "+t.getMessage(), "OK", null);
                } else {
                    Dialog.show("Failed", "The test failed.", "OK", null);
                }

            }
        } catch (Exception ex) {
            Log.e(ex);
        }
    }
    
    public void stop() {
        current = getCurrentForm();
        if(current instanceof Dialog) {
            ((Dialog)current).dispose();
            current = getCurrentForm();
        }
    }
    
    public void destroy() {
    }
    
    private void testCookies(String baseUrl) throws IOException {
        
        // Test scripts available at https://github.com/shannah/cn1-tests-server-side-scripts/tree/master/cookie
        // Should be run on a server that supports PHP
        Cookie.clearCookiesFromStorage();
        //String baseUrl = BASE_URL;
        String clearCookiesUrl = baseUrl +"/reset.php";
        String setCookiesUrl = baseUrl + "/set.php";
        String checkCookiesUrl = baseUrl + "/check.php";
        String setCookiesUrlSession = baseUrl + "/set_session.php";

        // Try without native cookie store
        ConnectionRequest.setUseNativeCookieStore(false);
        ConnectionRequest.fetchJSON(clearCookiesUrl);
        Map<String,Object> res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        System.out.println(res);
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));
        ConnectionRequest.fetchJSON(setCookiesUrl);
        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");

        // Now check that session cookies (no explicit expiry) are set correctly
        ConnectionRequest.fetchJSON(clearCookiesUrl);
        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));
        ConnectionRequest.fetchJSON(setCookiesUrlSession);
        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");

        // Try with native cookie store
        ConnectionRequest.setUseNativeCookieStore(true);
        ConnectionRequest.fetchJSON(clearCookiesUrl);
        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));
        ConnectionRequest.fetchJSON(setCookiesUrl);
        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");

        // Now check that session cookies (no explicit expiry) are set correctly
        ConnectionRequest.fetchJSON(clearCookiesUrl);
        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));
        ConnectionRequest.fetchJSON(setCookiesUrlSession);
        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");
        
        Throwable[] t = new Throwable[1];
        // Now test a different cookie date format.
        ConnectionRequest req = new ConnectionRequest() {

            @Override
            protected void handleException(Exception err) {
                Log.p("handling exception "+err);
                t[0] = err;
            }

            @Override
            protected void handleRuntimeException(RuntimeException err) {
                Log.p("handling runtime exception "+err);
                t[0] = err;
            }

            @Override
            protected void handleErrorResponseCode(int code, String message) {
                Log.p("Error response "+code+", "+message);
            }
            
            
            
        };
        
       
        String oldProp = (String)Display.getInstance().getProperty("com.codename1.io.ConnectionRequest.throwExceptionOnFailedCookieParse", null);
        Display.getInstance().setProperty("com.codename1.io.ConnectionRequest.throwExceptionOnFailedCookieParse", "true");
        req.setUrl(baseUrl + "/test_rfc822cookie.php");
        req.setFollowRedirects(true);
        req.setPost(false);
        req.setDuplicateSupported(true);
        
        //req.setFailSilently(true);
        try {
            NetworkManager.getInstance().addToQueueAndWait(req);
            
        } finally {
            //NetworkManager.getInstance().removeErrorListener(errorListener);
            Display.getInstance().setProperty("com.codename1.io.ConnectionRequest.throwExceptionOnFailedCookieParse", oldProp);
        }
        TestUtils.assertTrue(req.getResponseCode() == 200, "Unexpected response code.  Expected 200 but found "+req.getResponseCode());
        TestUtils.assertTrue(t[0] == null, t[0] != null ? ("Exception was thrown getting URL "+t[0].getMessage()):"");


    }
    
     private static class BrowserStatus {
        private static final int LOADING=0;
        private static final int READY=1;
        int status;
        String content;
        final BrowserComponent bc;

        BrowserStatus(BrowserComponent bc) {
            this.bc = bc;
            bc.addWebEventListener("onLoad", e->{
                ready(bc.executeAndReturnString("document.body.innerHTML"));
            });
        }

        synchronized void reset() {
            content = null;
            status = LOADING;
        }
        synchronized void ready(String content) {
            status = READY;
            this.content = content;
            notifyAll();
        }

        void waitReady() {
            while (status != READY) {
                Display.getInstance().invokeAndBlock(()->{
                    synchronized(BrowserStatus.this) {
                        Util.wait(BrowserStatus.this, 2000);
                    }
                });
            }
        }

        Map<String,Object> getJSONContent() throws IOException {
            JSONParser p = new JSONParser();
            String c = content.substring(content.indexOf("{"), content.lastIndexOf("}")+1);
            return p.parseJSON(new InputStreamReader(new ByteArrayInputStream(c.getBytes("UTF-8")), "UTF-8"));
        }
    }
     
    public class UnitTest extends AbstractTest {
        private String baseUrl = BASE_URL;
        private Throwable exception;
        
        UnitTest(String baseUrl) {
            this.baseUrl = baseUrl;
        }
        
        @Override
        public boolean runTest() throws Exception {
            try {
                testCookies(baseUrl);
                testCookiesInBrowserComponent(baseUrl);
            } catch (Throwable t) {
                exception = t;
                return false;
            }
            return true;
        }
        
        public Throwable getException() {
            return exception;
        }
    }
     
    
    private void testCookiesInBrowserComponent(String baseUrl) throws IOException {
        Cookie.clearCookiesFromStorage();
        Form f = new Form("CookiesInBrowser");
        String formName = "CookiesInBrowser";
        f.setName(formName);
        f.setLayout(new BorderLayout());
        BrowserComponent bc = new BrowserComponent();
        f.add(BorderLayout.CENTER, bc);
        f.show();
        TestUtils.waitForFormName(formName, 2000);
        //String baseUrl = BASE_URL;
        String clearCookiesUrl = baseUrl +"/reset.php";
        String setCookiesUrl = baseUrl + "/set.php";
        String checkCookiesUrl = baseUrl + "/check.php";
        String setCookiesUrlSession = baseUrl + "/set_session.php";



        final BrowserStatus status = new BrowserStatus(bc);
        bc.setURL(clearCookiesUrl);
        status.waitReady();
        status.reset();
        bc.setURL(checkCookiesUrl);
        status.waitReady();
        Map<String,Object> res = status.getJSONContent();
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));
        status.reset();
        bc.setURL(setCookiesUrl);
        status.waitReady();
        status.reset();
        bc.setURL(checkCookiesUrl);
        status.waitReady();
        res = status.getJSONContent();

        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");
        status.reset();
        bc.setURL(clearCookiesUrl);
        status.waitReady();
        status.reset();
        bc.setURL(checkCookiesUrl);
        status.waitReady();
        res = status.getJSONContent();
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));
        status.reset();

        bc.setURL(setCookiesUrlSession);
        status.waitReady();

        status.reset();
        bc.setURL(checkCookiesUrl);
        status.waitReady();
        res = status.getJSONContent();
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");

        // Now let's try to share cookies between the browser component and
        // a connection request.

        ConnectionRequest.setUseNativeCookieStore(true);
        Cookie.clearCookiesFromStorage();

        BrowserComponent bc2 = new BrowserComponent();
        bc.getParent().replace(bc, bc2, null);
        bc = bc2;
        f.revalidate();

        final BrowserStatus status2 = new BrowserStatus(bc);
        bc.setURL(clearCookiesUrl);
        status2.waitReady();
        status2.reset();

        // First verify that the cookie is not set in either browser or connection request

        bc.setURL(checkCookiesUrl);
        status2.waitReady();
        res = status2.getJSONContent();
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));


        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));

        // Next let's set the cookie in the browser, and verify that it is set in both
        // browser and connection request.
        status2.reset();
        bc.setURL(setCookiesUrl);
        status2.waitReady();

        status2.reset();
        bc.setURL(checkCookiesUrl);
        status2.waitReady();
        res = status2.getJSONContent();
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");

        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");


        // Now let's delete the cookie in the browser and verify that it is deleted in
        // both the browser and connection request.
        status2.reset();
        bc.setURL(clearCookiesUrl);
        status2.waitReady();

        status2.reset();
        bc.setURL(checkCookiesUrl);
        status2.waitReady();
        res = status2.getJSONContent();
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));

        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertBool(null == res.get("cookieval"), "Cookie should be null after clearing cookies but was "+res.get("cookieval"));

        // Now let's set the cookie in the ConnectionRequest and verify that it is set in both
        // connection request and browser.

        ConnectionRequest.fetchJSON(setCookiesUrl);
        res = ConnectionRequest.fetchJSON(checkCookiesUrl);
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");

        status2.reset();
        bc.setURL(checkCookiesUrl);
        status2.waitReady();
        res = status2.getJSONContent();
        TestUtils.assertEqual("hello", res.get("cookieval"), "Cookie set to incorrect value.");

    }

}
